from logger import logger


class StockAnalysisCommand:
    def execute(self, df):
        raise NotImplementedError("Subclasses should implement this method")


class CheckFallingDaysCommand(StockAnalysisCommand):
    def __init__(self, days=3):
        if days < 0:
            raise ValueError("Days cannot be negative")
        self.days = days

    def execute(self, df):
        # 确保数据集足够长
        if len(df) >= self.days:
            # 检查最后几天是否都是阴线
            last_days = df.tail(self.days)
            return (last_days['close'] < last_days['open']).all()
        else:
            return False


# 最后一个交易日是中等光脚阴线
class CheckMediumShadowCommand(StockAnalysisCommand):
    # medium_fall_threshold 实体长度，shadow_threshold 影线长度
    def __init__(self, shadow_threshold=0.1, medium_fall_threshold=0.03):
        self.shadow_threshold = shadow_threshold
        self.medium_fall_threshold = medium_fall_threshold

    def execute(self, df):
        if not df.empty:
            # 获取最后一个交易日的数据
            last_day = df.iloc[-1]
            # 检查收盘价是否低于开盘价
            if last_day['close'] < last_day['open']:
                # 计算跌幅
                fall_percentage = (last_day['open'] - last_day['close']) / last_day['open']
                # 检查跌幅是否在中等范围内
                if 0 < fall_percentage < self.medium_fall_threshold:
                    # 检查下影线是否很短
                    if (last_day['close'] - last_day['low']) < (last_day['open'] - last_day['close']) * self.shadow_threshold:
                        return True
        return False


class CheckMediumFallingDayCommand(StockAnalysisCommand):
    def __init__(self, medium_fall_threshold=0.03,low_threshold=0.015):
        self.medium_fall_threshold = medium_fall_threshold
        self.low_threshold = low_threshold

    def execute(self, df):
        if len(df) < 2:
            return False
        # 检查倒数第二天是否是中阴线
        second_last_day = df.iloc[-2]
        fall_percentage = (second_last_day['open'] - second_last_day['close']) / second_last_day['open']
        return self.low_threshold < fall_percentage < self.medium_fall_threshold


class CheckDojiCommand(StockAnalysisCommand):
    def __init__(self, doji_threshold=0.2,tail_num = 1):
        self.doji_threshold = doji_threshold
        self.tail_num = tail_num

    def execute(self, df):
        if len(df) < self.tail_num:
            return False
        # 检查最后一天是否是十字星
        last_day = df.iloc[-self.tail_num:]
        # if last_day.shape[0] == 1:
        #     return self.is_doji(last_day)
        # else:
        return all(self.is_doji(row) for idx,row in last_day.iterrows())

    def is_doji(self, row):
        # 检查是否是十字星
        body = abs(row['open'] - row['close'])
        total_range = row['high'] - row['low']
        if total_range > 0:
            # print(body/total_range)
            return (body / total_range) < self.doji_threshold
        else:
            return False

    # def are_last_two_days_doji(self):
    #     # 检查最后两天是否都是十字星
    #     last_two_days = self.df.iloc[-2:]
    #     return self.is_doji(last_two_days.iloc[0]) and self.is_doji(last_two_days.iloc[1])


class CheckVerticalLineCommand(StockAnalysisCommand):
    def __init__(self, lower_shadow_threshold=1, upper_shadow_threshold=0.1):
        self.lower_shadow_threshold = lower_shadow_threshold
        self.upper_shadow_threshold = upper_shadow_threshold

    def execute(self, df):
        if not df.empty:
            # 检查最后一天是否是锤子线
            last_day = df.iloc[-1]
            # 计算实体大小
            body = abs(last_day['open'] - last_day['close'])
            # 计算下影线和上影线
            lower_shadow = min(last_day['open'], last_day['close']) - last_day['low']
            upper_shadow = last_day['high'] - max(last_day['open'], last_day['close'])
            # 检查是否符合锤子线条件
            return lower_shadow >= body * self.lower_shadow_threshold and upper_shadow <= body * self.upper_shadow_threshold
        return False



# 倒数第二天是中阴线，最后一天是十字星和锤子线
class CheckCombinedConditionsCommand(StockAnalysisCommand):
    def __init__(self, medium_fall_threshold=0.03, doji_threshold=0.01, vertical_line_threshold=0.05):
        self.medium_fall_threshold = medium_fall_threshold
        self.doji_threshold = doji_threshold
        self.vertical_line_threshold = vertical_line_threshold
        self.medium_falling_day_command = CheckMediumFallingDayCommand(medium_fall_threshold)
        self.doji_command = CheckDojiCommand(doji_threshold)
        self.vertical_line_command = CheckVerticalLineCommand(vertical_line_threshold)

    def execute(self, df):
        # 检查倒数第二天是否是中阴线
        medium_falling_day_result = self.medium_falling_day_command.execute(df)
        # 检查最后一天是否是十字星或垂直线
        doji_or_vertical_line_result = self.doji_command.execute(df) or self.vertical_line_command.execute(df)

        # 如果倒数第二天是中阴线，并且最后一天是十字星或垂直线，则返回True
        return medium_falling_day_result and doji_or_vertical_line_result


class CheckSmallCandlesticksCommand(StockAnalysisCommand):
    def __init__(self, small_movement_threshold=0.01,tail_num=4):
        self.small_movement_threshold = small_movement_threshold
        self.tail_num = tail_num

    def execute(self, df):
        if len(df) < self.tail_num:
            return False
        # 检查最后4天的数据
        last_four_days = df.iloc[-self.tail_num:]
        # 定义小阴小阳线的条件
        def is_small_movement(day):
            return abs(day['open'] - day['close']) / day['open'] < self.small_movement_threshold
        # 检查每一天是否都是小阴小阳线
        return all(is_small_movement(day) for index,day in last_four_days.iterrows())


class analyze():
    def __init__(self):
        self.StockAnalysisCommand = [CheckFallingDaysCommand(),CheckMediumShadowCommand(),CheckCombinedConditionsCommand(),CheckSmallCandlesticksCommand(),CheckDojiCommand(tail_num=2)]

    def excute(self, code,df):
        for analysis_command in self.StockAnalysisCommand:
            if analysis_command.execute(df):
                logger.info("股票代码："+code+" - "+analysis_command.__class__.__name__)
                return True
        return False



